package cz.czu.pef.DataModelling;

import java.util.*;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.persistence.*;

import org.hibernate.annotations.Index;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import cz.czu.pef.DataModelling.utils.Lazy;

/**
 * Person entity. This entity represents Person with undertaken trainings and medicalExaminations.
 * 
 * @author rstribrn
 *
 */
@Entity
@Table(name="PEOPLE")
@NamedQueries({
	@NamedQuery(name="findPersonByName", query="SELECT p FROM Person p WHERE p.firstName = :firstName AND p.lastName = :lastName")
})
@ManagedBean(name="person")
@ViewScoped
@Component
@Scope(value="prototype")
public class Person extends IdPersistenceObject
{
	private static final long serialVersionUID = -3846628860514746169L;

	/*
	 * Entity attributes
	 */
	@Column(name="FIRST_NAME", nullable=false, length=200)
	@Index(name="IDX_PERSON_FN")
	private String firstName;

	@Column(name="LAST_NAME", nullable=false, length=200)
	@Index(name="IDX_PERSON_LN")
	private String lastName;

	@Column(name="BIRTH_DATE", nullable=true)
	@Index(name="IDX_PERSON_BIRTHDATE")
	private Date birthDate;

	@ManyToOne(fetch=FetchType.EAGER, cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH},optional=true)
	private Address address;

	/*
	 * Relations
	 */
	@ManyToMany(fetch = FetchType.LAZY, targetEntity = MedicalExamination.class, cascade={CascadeType.ALL}) // all cascades!
	@JoinTable(name="PERSON_MEDICAL_EXAMINATIONS")
	private Set<MedicalExamination> medicalExaminations;

	@ManyToMany(fetch = FetchType.LAZY, targetEntity = Training.class, cascade={CascadeType.ALL}) // all cascades!
	@JoinTable(name="PERSON_TRAININGS")
	private Set<Training> trainings;

	@OneToMany(fetch=FetchType.LAZY, targetEntity=Machinery.class, mappedBy="personOwner") // no cascade!
	private Set<Machinery> machineries;

	@OneToMany(fetch=FetchType.EAGER, targetEntity=Employee.class, mappedBy="person") // no cascade!
	private Set<Employee> employees;

	protected Person()
	{
		super();
	}

	public Person(final String fn, final String ln)
	{
		setFirstName(fn);
		setLastName(ln);
	}

	public Person(final String fn, final String ln, Address address)
	{
		setFirstName(fn);
		setLastName(ln);
		setAddress(address);
	}

	public Address getAddress()
	{
		return address;
	}

	public String getFirstName()
	{
		return firstName;
	}


	public String getLastName()
	{
		return lastName;
	}

	public void setAddress(Address address)
	{
		this.address = address;
	}

	public void setFirstName(final String firstName)
	{
		this.firstName = firstName;
	}

	public void setLastName(final String lastName)
	{
		this.lastName = lastName;
	}

	
	public Date getBirthDate()
	{
		return birthDate;
	}

	
	public void setBirthDate(Date birthDate)
	{
		this.birthDate = birthDate;
	}

	@Lazy
	@Transactional(readOnly=true, propagation=Propagation.REQUIRED)
	public Set<Employee> getEmployees()
	{
		return employees;
	}
	@Lazy
	@Transactional(readOnly=true, propagation=Propagation.REQUIRED)
	public List<Employee> getEmployeesAsList()
	{
		return new ArrayList<Employee>(getEmployees());
	}

	
	public void setEmployees(Set<Employee> employees)
	{
		this.employees = employees;
	}

	
	@Lazy
	@Transactional(readOnly=true, propagation=Propagation.REQUIRED)
	public Set<Machinery> getMachineries()
	{
		return machineries;
	}
	@Lazy
	@Transactional(readOnly=true, propagation=Propagation.REQUIRED)
	public List<Machinery> getMachineriesAsList()
	{
		return new ArrayList<Machinery>(getMachineries());
	}

	@Lazy
	@Transactional(readOnly=true, propagation=Propagation.REQUIRED)
	public Set<MedicalExamination> getMedicalExaminations()
	{
		return medicalExaminations;
	}

	@Lazy
	@Transactional(readOnly=true, propagation=Propagation.REQUIRED)
	public List<MedicalExamination> getMedicalExaminationsAsList()
	{
		return new ArrayList<MedicalExamination>(getMedicalExaminations());
	}

	public void addMedicalExamination(MedicalExamination medicalExamination)
	{
		if (medicalExaminations == null)
			medicalExaminations = new HashSet<MedicalExamination>();
		medicalExaminations.add(medicalExamination);
	}

	@Lazy
	@Transactional(readOnly=true, propagation=Propagation.REQUIRED)
	public Set<Training> getTrainings()
	{
		return trainings;
	}

	@Lazy
	@Transactional(readOnly=true, propagation=Propagation.REQUIRED)
	public List<Training> getTrainingsAsList()
	{
		return new ArrayList<Training>(getTrainings());
	}

	public void addTraining(Training training)
	{
		if (trainings == null)
			trainings = new HashSet<Training>();

		trainings.add(training);
	}

	/**
	 * Can be deleted after no machinery is owner directly by this person && person is not employee (external_worker).
	 */
	@Override
	public boolean canBeRemoved()
	{
		return (getMachineries().size() == 0) && getEmployees().size() == 0;
	}

	@Lazy
	@Transactional(readOnly=true, propagation=Propagation.REQUIRED)
	@Override
	public String toString()
	{
		StringBuilder builder = new StringBuilder();
		builder.append("Person [getFirstName()=");
		builder.append(getFirstName());
		builder.append(", getLastName()=");
		builder.append(getLastName());
		builder.append(", getBirthDate()=");
		builder.append(getBirthDate());
		builder.append(", getAddress()=");
		builder.append(getAddress());
		builder.append(", getMedicalExaminations()=");
		builder.append(getMedicalExaminations());
		builder.append(", getTrainings()=");
		builder.append(getTrainings());
		builder.append("]");
		return builder.toString();
	}

}